Set Up

Initilisation

Load Libraries

library(tidyverse)
library(corrplot) # For correlation plots
library(FactoMineR) # For PCA
library(factoextra) # For PCA plots
library(vegan) # For CCA
library(ggplot2)
library(rsample) # For sampling of data (split, analysis,..)
library(rpart)
library(rpart.plot)
library(randomForest)
library(tibble)
library(tidyr)
library(gridExtra)
library(factoextra)
library(caret)
library(cluster)

Knitting Configuration

# Set to render chunks during the knitting process
knitr::opts_chunk$set(echo = TRUE)

Set Up the Environment

# Clear Environment
rm(list = ls())

# Set Working Directory
setwd("/Users/eamon/Desktop/MathToolsProject/R_Project/MathematicalTools/")

# Load Data
data <- read.csv("lizard.csv")

View the Data

str(data)
'data.frame':   740 obs. of  24 variables:
 $ Species                        : chr  "Agama Agama" "Agama Agama" "Agama hispida" "Caimanops amphiboluroides" ...
 $ Genus                          : chr  "Agama" "Agama" "Agama" "Caimanops" ...
 $ Family                         : chr  "Agamidae" "Agamidae" "Agamidae" "Agamidae" ...
 $ Population                     : chr  "Suacocco, Liberia 1952-1953" "Ibadan, NIgeria" "Kalahari Desert, Africa, 1969-1970" "Australia" ...
 $ Longitude                      : num  -9.05 3.92 21.25 119.1 119.62 ...
 $ Latitude                       : num  7.08 7.4 -27.03 -28.5 -27.08 ...
 $ Average.Female.adult.weight..g.: num  NA NA 31.9 NA 10 ...
 $ SD.Female.adult.weight..g.     : chr  NA NA "6.16" NA ...
 $ Sample.Size.Female.adult.weight: int  NA NA 28 NA 17 75 NA 41 741 120 ...
 $ Mean.F.SVL.adults..mm.         : num  85 97 94.8 94 62.9 ...
 $ SD.F.SVL.adults..mm.           : num  NA NA 6.91 7.07 4.43 ...
 $ Sample.size.Mean.F.SVL.adults  : int  63 NA 51 2 18 75 NA 41 756 120 ...
 $ F.SVL.at.maturity..mm.         : num  75 NA 76 89 57 44 48 49 47 51 ...
 $ Offspring.SVL..mm.             : num  32 NA 29 38 22 23 24 25 22 26 ...
 $ Mean.Clutch.Size               : num  7.5 5.5 13.4 12 5.36 ...
 $ Sample.size.Clutch.Size.       : int  NA NA 45 2 14 28 NA 13 40 39 ...
 $ Mode.of.reproduction           : chr  "Oviparous" "Oviparous" "Oviparous" "Oviparous" ...
 $ Clutches.per.year              : num  2 NA NA NA NA NA 2 NA NA NA ...
 $ Clutch.Frequency               : num  2 2 1 1 1 1 2 1 2 2 ...
 $ RCM                            : num  NA NA 0.305 NA 0.166 ...
 $ Foraging.Mode                  : chr  "Sit and wait" "Sit and wait" "Sit and wait" "Sit and wait" ...
 $ Distribution                   : chr  "Tropical" "Tropical" "Temperate" "Temperate" ...
 $ Prefered.Habitat.Type          : chr  "Saxicolous" "Saxicolous" "Semi-arboreal" "Semi-arboreal" ...
 $ Source                         : chr  "(Harris 1964)" "(Daniel 1960)" "(Pianka 1986)" "(Pianka 2013d)" ...

Cleaning

Change Variable Types

# This variables was input as a character, but it should be numeric
data$SD.Female.adult.weight..g. <- as.numeric(data$SD.Female.adult.weight..g.)
Warning: NAs introduced by coercion

Set Blanks in Categorical Variables as NA

# Convert the blank values of categorical variables to NA
data<-data %>%
  mutate(across(where(is.character), ~ na_if(.x, ""))) %>%
  mutate(across(where(is.character), as.factor))

Check for and Remove Duplicates

# Save duplicates as an object
duplicates <- data[duplicated(data), ]

# Remove duplicates
data <- data[!duplicated(data), ]

Visualise the Proportion of Each Variable Which is NA

By doing this we found that most of the numeric variables had a high proportion of NA’s, with some such as RCM having over 50%. Therefore exclusion of all rows containing NA’s was likely to drastically reduce the size dataset and cause data loss. The number of rows in the original dataset was 734 and the number after exclusion of all NAs was 146. Therefore the NA’s were removed separately for each analysis using only the variables required for that analysis, which helped to reduce data loss.

# Calculate percentage of NA values for each column
na_percentage <- colSums(is.na(data)) / nrow(data) * 100

# Convert NA percentages to a data frame
na_df <- data.frame(
  Column = names(na_percentage),
  NA_Percentage = na_percentage
)

# Create bar plot
ggplot(na_df, aes(x = Column, y = NA_Percentage)) +
  geom_bar(stat = "identity", fill = "skyblue") +
  labs(
    title = "Percentage of NA Values per Column",
    x = "Columns",
    y = "Percentage (%)"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1) # Rotate x-axis labels
  )


# Print the number of rows in the full dataset
print(paste("Number of rows in full dataset =", nrow(data)))
[1] "Number of rows in full dataset = 734"
# Print the number of rows after NA removal
print(paste("Number of rows after NA removal on entire dataset =", nrow(na.omit(data))))
[1] "Number of rows after NA removal on entire dataset = 146"

PCA

Choose the Variables to be Used in the PCA

This saves the variables to be used in the PCA as an object. The variables used are easily altered by commenting them in or out. Only numerical variables are considered as candidates as PCA can only use numerical variables. Where two variables describe the same trait and/or are correlated, one of them was removed. This was done in order to avoid bias in the PCA by over emphasising particular traits.

“F.SVL.at.maturity..mm” describes the same trait as “Mean.F.SVL.adults..mm.”. A correlation of these two variables revealed a 97% correlation. Therefore, “F.SVL.at.maturity..mm.” was randomly chosen to be removed.

Clutch Frequency and Clutches Per Year were also correlated and describe the same trait and therefore Clutch Frequency was removed.

Sample sizes and standard deviations were removed from the analysis as they were not thought to be relevant.

variables_to_use_PCA <- c(
  ### Location
  #"Longitude",
  #"Latitude",
  
  ### Means and Numerical
  #"Average.Female.adult.weight..g.",
  "Mean.F.SVL.adults..mm.",
  #"F.SVL.at.maturity..mm.",
  "Offspring.SVL..mm.",
  "Mean.Clutch.Size",
  "Clutches.per.year",
  #"Clutch.Frequency",
  "RCM"
  
  ### Standard Deviations and Sample Sizes
  #"SD.Female.adult.weight..g.",
  #"SD.F.SVL.adults..mm.",
  #"Sample.Size.Female.adult.weight",
  #"Sample.size.Mean.F.SVL.adults",
)

Investigation of the Similar Variables

To investigate the correlations between variables.

Create New Dataset retaining only the PCA variables

Here a new subset of the data is created for the PCA. The PCA data was cleaned of all rows containing NA’s and standardised such that each variable has mean 0 and variance 1 (which allows for comparison of variables on different scales in the PCA). A second data set was created containing the rows retained after NA omition of the PCA data, but containing all the original variables. This is later used to color the PCA visualisations with categorical variables.

# Create the new PCA dataset
data_PCA <- data %>%
  select(any_of(variables_to_use_PCA)) %>% # filter variables
  na.omit() %>% # omit NA's
  mutate_all(.funs = scale) %>% # standardise the data
  rownames_to_column(var = "row_id") # set rownames as a column

# Create ancillary dataset with all original variables but filtered to keep only rows present in the PCA dataset
# This will be used for coloration of the PCA graphs with categorical variables

data_PCA_all_vars <- data %>%
  rownames_to_column(var = "row_id") %>% # set rownames as a column
  filter(row_id %in% data_PCA$row_id) %>% # filter the data to match rows of data_PCA
  column_to_rownames(var = "row_id") # Convert column back to rownames

# Convert the column back to rownames for the PCA data
data_PCA <- data_PCA %>%
  column_to_rownames(var = "row_id")

Visualise with a Correlation

This shows the correlation of each variable pairs in filtered PCA data. None of the variables are highly correlated with each other.

### CorrPlot
data_PCA %>% 
  cor(use="pairwise.complete.obs") %>% # Calculate the empirical correlation matrix
  corrplot() # Then graph this matrix

Run the PCA

Here the PCA is run and the results are presented in various forms including the output tables, a scree plot showing the percentage of explained variance by each principle component and the correlation of each variable with each principle component.

# Run the PCA
result_pca <- PCA(data_PCA, 
               scale.unit = TRUE, # Option to center and scale data (useless here)
               ncp = 18, # Number of components to keep (here, all)
               graph = FALSE)

# Results of the PCA
result_pca$eig
       eigenvalue percentage of variance cumulative percentage of variance
comp 1  1.9331037              38.662073                          38.66207
comp 2  1.6070287              32.140575                          70.80265
comp 3  0.7868455              15.736911                          86.53956
comp 4  0.4494887               8.989773                          95.52933
comp 5  0.2235334               4.470668                         100.00000
result_pca$var
$coord
                           Dim.1      Dim.2       Dim.3       Dim.4       Dim.5
Mean.F.SVL.adults..mm. 0.6976592  0.6159273  0.04570107 -0.18884879 -0.31008511
Offspring.SVL..mm.     0.3783690  0.8203961  0.18805831  0.29955692  0.24225387
Mean.Clutch.Size       0.8383756 -0.2943332 -0.13505408 -0.36570314  0.24190071
Clutches.per.year      0.3758802 -0.5275783  0.75400593  0.09854526 -0.04625082
RCM                    0.6775343 -0.4354788 -0.40326973  0.42501824 -0.08965820

$cor
                           Dim.1      Dim.2       Dim.3       Dim.4       Dim.5
Mean.F.SVL.adults..mm. 0.6976592  0.6159273  0.04570107 -0.18884879 -0.31008511
Offspring.SVL..mm.     0.3783690  0.8203961  0.18805831  0.29955692  0.24225387
Mean.Clutch.Size       0.8383756 -0.2943332 -0.13505408 -0.36570314  0.24190071
Clutches.per.year      0.3758802 -0.5275783  0.75400593  0.09854526 -0.04625082
RCM                    0.6775343 -0.4354788 -0.40326973  0.42501824 -0.08965820

$cos2
                           Dim.1      Dim.2       Dim.3       Dim.4       Dim.5
Mean.F.SVL.adults..mm. 0.4867283 0.37936643 0.002088587 0.035663864 0.096152777
Offspring.SVL..mm.     0.1431631 0.67304969 0.035365929 0.089734347 0.058686935
Mean.Clutch.Size       0.7028736 0.08663202 0.018239606 0.133738786 0.058515951
Clutches.per.year      0.1412859 0.27833883 0.568524941 0.009711168 0.002139139
RCM                    0.4590527 0.18964177 0.162626476 0.180640503 0.008038593

$contrib
                           Dim.1    Dim.2      Dim.3     Dim.4     Dim.5
Mean.F.SVL.adults..mm. 25.178595 23.60670  0.2654381  7.934319 43.014950
Offspring.SVL..mm.      7.405868 41.88162  4.4946469 19.963651 26.254214
Mean.Clutch.Size       36.359852  5.39082  2.3180669 29.753539 26.177722
Clutches.per.year       7.308761 17.32009 72.2536906  2.160492  0.956966
RCM                    23.746924 11.80077 20.6681576 40.187999  3.596149
# Plot the Scree Plot to Show Variance Explained by Each Dimension
fviz_eig(result_pca, choice = "variance")


# Plot the Correlation of Each Variable and the Dimension
corr_plot_pca <-corrplot(result_pca$var$cor)

corr_plot_pca
$corr
                           Dim.1      Dim.2       Dim.3       Dim.4       Dim.5
Mean.F.SVL.adults..mm. 0.6976592  0.6159273  0.04570107 -0.18884879 -0.31008511
Offspring.SVL..mm.     0.3783690  0.8203961  0.18805831  0.29955692  0.24225387
Mean.Clutch.Size       0.8383756 -0.2943332 -0.13505408 -0.36570314  0.24190071
Clutches.per.year      0.3758802 -0.5275783  0.75400593  0.09854526 -0.04625082
RCM                    0.6775343 -0.4354788 -0.40326973  0.42501824 -0.08965820

$corrPos

$arg
$arg$type
[1] "full"

Plot the Individuals with the Variable Arrows and the Categorical Coloring

By altering the object Color_label we were able to check the effect of coloring with each of the four main categorical variables (Distribution, Habitat type, Foraging mode and Reproductive type). It was found that distribution produced the clearest clustering with the least noise. Therefore distribution was taken forward as a potential categorical variable which can account for much of the variation. This code could of been shortened using a loop, however it affected the scaling of the axes and the font size and we were unable to resolve this issue.

# Define Graphing Function
plot_pca_with_color <- function(color_label) {
  p1 <- fviz_pca_biplot(result_pca, axes = c(1, 2), col.ind = data_PCA_all_vars[[color_label]], col.var = "black")
  p2 <- fviz_pca_biplot(result_pca, axes = c(1, 3), col.ind = data_PCA_all_vars[[color_label]], col.var = "black")
  p3 <- fviz_pca_biplot(result_pca, axes = c(2, 3), col.ind = data_PCA_all_vars[[color_label]], col.var = "black")
  p4 <- fviz_pca_biplot(result_pca, axes = c(3, 4), col.ind = data_PCA_all_vars[[color_label]], col.var = "black")
  grid.arrange(p1, p2, p3, p4, nrow = 2, ncol = 2)
}

# Use function with Distribution
dist_plot <- plot_pca_with_color("Distribution")

# Save Plot
ggsave("PCA_plot_distribution.png", plot = dist_plot, width = 10, height = 8.4, units = "in", dpi = 500)
# Print plot
dist_plot
TableGrob (2 x 2) "arrange": 4 grobs
# Plot for other categorical variables
dist_plot <- plot_pca_with_color("Prefered.Habitat.Type")

dist_plot <- plot_pca_with_color("Mode.of.reproduction")

dist_plot <- plot_pca_with_color("Foraging.Mode")

K-Means Clustering

Create Clusters for 2-5 Groups Out of Curiosity

# Create clusters for 2 Groups - To Match Distribution Levels
kmeans_2groups <- data_PCA %>%
  kmeans(centers=2, iter.max = 1000)

# Calculate the within group inertia
# Within Group Inertia
kmeans_2groups$totss
[1] 1005
kmeans_2groups$tot.withinss
[1] 760.1047
kmeans_2groups$withinss
[1] 481.5935 278.5113
kmeans_2groups$betweenss
[1] 244.8953
# Create Clusters For More Groups Out Curiosity
first_kmeans <- data_PCA %>% 
  kmeans(centers = 4, iter.max = 1000)

kmeans_5groups<- data_PCA %>%
  kmeans(centers = 5, iter.max = 1000)

kmeans_4groups<- data_PCA %>%
  kmeans(centers = 4, iter.max = 1000)

kmeans_3groups<- data_PCA %>%
  kmeans(centers=3, iter.max = 1000)

# Extract the Clustering Groups
clusters_5groups <- kmeans_5groups$cluster
clusters_4groups <- kmeans_4groups$cluster
clusters_3groups <- kmeans_3groups$cluster
clusters_2groups <- kmeans_2groups$cluster

Plot a Comparison of the 2 Group K-Means Clustering vs The Coloration Applied by the “Distribution” Variable

The k-means method found clusters which visually seem very similar to those applied by the Distribution category.

Color_label = "Distribution"

# Define a color palette for consistency
custom_palette <- c("red", "blue", "green", "orange")

# Save each of the plots 
p1A <- fviz_pca_biplot(result_pca, axes = c(1, 2), col.ind = as.factor(clusters_2groups), col.var = "black", title = "PC1 & PC2 - Clustering") + ylim(-4, 4)
p1B <- fviz_pca_biplot(result_pca, axes = c(1, 2), col.ind = data_PCA_all_vars[[Color_label]], col.var = "black", title = "PC1 & PC2 - Distribution")+ ylim(-4, 4) 
p2A <- fviz_pca_biplot(result_pca, axes = c(1, 3), col.ind = as.factor(clusters_2groups), col.var = "black", title = "PC1 & PC3 - K-Means Clustering")+ ylim(-4, 4) 
p2B <- fviz_pca_biplot(result_pca, axes = c(1, 3), col.ind = data_PCA_all_vars[[Color_label]], col.var = "black", title = "PC1 & PC3 - Distribution")+ ylim(-4, 4)
p3A <- fviz_pca_biplot(result_pca, axes = c(2, 3), col.ind = as.factor(clusters_2groups), col.var = "black", title = "PC2 & PC3 - K-Means Clustering")+ ylim(-4, 4)
p3B <- fviz_pca_biplot(result_pca, axes = c(2, 3), col.ind = data_PCA_all_vars[[Color_label]], col.var = "black", title = "PC2 & PC3 - Distribution")+ylim(-4, 4)

# Plot each axis combination
all_4_plots<-grid.arrange(p1A, p1B, p2A, p2B, p3A, p3B, nrow = 4, ncol=2)


# Save a ggplot object
ggsave("Clustering-Kmeans.png", plot = all_4_plots, width = 11, height = 10, units = "in", dpi = 500)

Comparison of K-Means Clusterings and Distribution Groupings Using Chi-Squared Test of Independence

# Convert kmeans output to a dataframe
data_with_clusters <- data_PCA %>%
  as.data.frame() %>%                  # Ensure it's a dataframe
  mutate(Cluster = kmeans_2groups$cluster) %>% # Add the cluster assignments
  mutate(Distribution = data_PCA_all_vars$Distribution)
data_with_clusters

# Contingency table
table_kmeans_vs_distribution <- table(kmeans_clusters = data_with_clusters$Cluster, distribution = data_with_clusters$Distribution)

# Print the table
print(table_kmeans_vs_distribution)
               distribution
kmeans_clusters Temperate Tropical
              1        33       93
              2        37       39
# Perform chi-square test
chisq_test <- chisq.test(table_kmeans_vs_distribution)
print(chisq_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  table_kmeans_vs_distribution
X-squared = 9.6222, df = 1, p-value = 0.001922

Unsupervised Learning

##Split Data ### Specify Variables to Use

# Specify the Variables to Use in the Random Forests Analysis
variables_to_use_RF <- c(
  ### Categorical Variables
  "Mode.of.reproduction",
  "Distribution",
  "Prefered.Habitat.Type",
  "Foraging.Mode",
  
  ### Location
  #"Longitude",
  #"Latitude",
  
  ### Numerical Variables
  #"Average.Female.adult.weight..g.",
  "Mean.F.SVL.adults..mm.",
  #"F.SVL.at.maturity..mm.",
  "Offspring.SVL..mm.",
  "Mean.Clutch.Size",
  "Clutches.per.year",
  #"Clutch.Frequency",
  "RCM"
  
  ### Standard Deviations and Sample Sizes
  #"SD.F.SVL.adults..mm.",
  #"Sample.size.Mean.F.SVL.adults",
  #"SD.Female.adult.weight..g.",
  #"Sample.Size.Female.adult.weight"
)

###Filter Data Variables Filter out the data to only keep variables to be used in the random forests and KNN analyses. Remove Nas.

data_RF <- data %>%
  select(any_of(variables_to_use_RF)) %>% # Select variables
  na.omit() %>%
  droplevels()

Initial Split

Split the data such that 80% is used for the training and 20% is unseen.

split_data <- data_RF %>%
  initial_split(prop = 0.8, strata = "Distribution")

# Train and Test Data
train_data <- analysis(split_data)
test_data <- assessment(split_data)

Random Forests

Choose the Optimum Hyperparameter ‘mtry’.

This chunk enables us to determine the optimum value of the hyperparameter mtry. Here it is found to be at the highest for the mtry values 4, 5 and 6.

# Initialisation
params_CV <- trainControl(method = "repeatedcv", 
                          number = 10, 
                          repeats = 5)

predictor_forest <- caret::train(Distribution ~ ., 
                                 data = data_RF, 
                                 method = "rf", 
                                 metric = "Accuracy", 
                                 trControl = params_CV, 
                                 tuneGrid = expand.grid(mtry = 2:12))

as.data.frame(predictor_forest$results) %>% 
  ggplot(aes(x = mtry, y = Accuracy)) +
  geom_point()

Create the RF

Use 5000 random trees and mtry value 3.

forest <- randomForest(Distribution ~ ., # Formula for prediction
                      data = train_data, # Data for training
                      ntree = 5000, # Number of trees
                      maxnodes = 10, # Number of maximum leaves for each tree
                      mtry = 3, # Number of variables for each tree
                      importance = TRUE) # Computation of importance

Test the Accuracy

Accuracy found to be 0.829

# Fast way of computing accuracy with the pipe operator %>% 
confusion_matrix <-predict(forest, newdata = select(test_data, - Distribution)) %>% 
  table(prediction = ., truth = test_data$Distribution) 

accuracy <- confusion_matrix %>%
  {sum(diag(.)) / sum(.)}

confusion_matrix
           truth
prediction  Temperate Tropical
  Temperate         9        0
  Tropical          5       27
accuracy
[1] 0.8780488

Find the Variable Importance

Found the Mean Clutch Size caused the highest mean decrease in accuracy and mean decrease in the Gini coefficient of impurity. Therefore it is the variable contributing the most to the classification.

png("Variable_importance.png", width = 12, height = 8, units = "in", res = 500)
varImpPlot(
  forest, 
  main = "Variable Importance Plot Random Forests",
  cex = 0.7,
  
)
dev.off()
null device 
          1 
varImpPlot(
  forest, 
  main = "Variable Importance Plot Random Forests",
  cex = 0.7  # Adjust font size
)

KNN

Split the Data Randomly

# We split the data (80% for training)
first_split <- initial_split(data_RF, prop = 0.8)
first_train_set <- analysis(first_split) 
dim(first_train_set); head(first_train_set)
[1] 161   9
first_test_set <- assessment(first_split) 
dim(first_test_set); head(first_test_set)
[1] 41  9

Appendix - CA and CCA

This section is not presented in the main text. We tried to do a CA and CCA but the lack of environmental variables proved to be an obstacle. Absolute latitude was averaged for each habitat in order to test the effect of latitude/climate on the association between families and particular habitats. However no association was found and we were unsure if averaging the value of latitudes for the habitats was appropriate.

Variables to use in the Analysis

variables_to_use_CCA <- c(
  #"Distribution",
  #"Foraging.Mode",
  #"Mode.of.reproduction",
  "Prefered.Habitat.Type",
  "Family",
  "Latitude"
)

Intiial Data Wrangling

# Select all four variables to be used in the CA and CCA, omit NAs
data_CCA <- data %>%
  # Select only the variables to use for the CA and CCA
  select(any_of(variables_to_use_CCA)) %>%
  # Exclude NAs
  na.omit() 

# Make a frequency table containing just 2 categorical variables for the CA
# With each pair of levels on a seperate row
freq_CA_long <- data_CCA %>%
  select(Family, Prefered.Habitat.Type) %>%
  table() %>%
  as_tibble %>%
  rename(Freq = "n",
         Habitat = "Prefered.Habitat.Type")

# Make a tibble with the count of each combination
freq_CA_short <- data_CCA %>%
  select(Prefered.Habitat.Type, Family) %>%
  count(Prefered.Habitat.Type, Family) %>%  
  pivot_wider(
    names_from = Family, 
    values_from = n,  
    values_fill = 0)

# Convert to dataframe and create rownames (for use creating average profile)
freq_CA_short_df <- freq_CA_short %>%
  as.data.frame() %>%  
  column_to_rownames(var = "Prefered.Habitat.Type")  

freq_CA_long
freq_CA_short
freq_CA_short_df

Represent in ggplot

ggplot(freq_CA_long) +
  aes(x = Family, y = Habitat, fill = Freq) + # fill says which column is used 
  # for filling
  geom_raster() + # represents in raster mode
  # and dress to make it pretty
  scale_fill_viridis_c() + # Change color scale
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

Calculate the Average Profile

# Create average profile  
average_profile <- colSums(freq_CA_short_df) / sum(freq_CA_short_df)
average_profile
      Dactyloidae  Gymnophthalmidae     Shinisauridae           Teiidae          Agamidae    Chamaeleonidae    Corytophanidae   Diplodactylidae 
      0.054570259       0.065484311       0.001364256       0.169167804       0.036834925       0.002728513       0.002728513       0.015006821 
       Gekkonidae     Hoplocercidae      Leiosauridae       Liolaemidae         Opluridae   Phrynosomatidae  Phyllodactylidae     Polychrotidae 
      0.053206003       0.001364256       0.006821282       0.028649386       0.001364256       0.111869031       0.031377899       0.005457026 
        Scincidae Sphaerodactylidae      Tropiduridae         Varanidae          Anguidae       Anniellidae        Lacertidae        Cordylidae 
      0.163710778       0.025920873       0.077762619       0.004092769       0.010914052       0.004092769       0.038199181       0.006821282 
    Crotaphytidae         Iguanidae    Leiocephalidae      Xenosauridae  Carphodactylidae     Eublepharidae    Helodermatidae       Pygopodidae 
      0.015006821       0.006821282       0.010914052       0.031377899       0.002728513       0.001364256       0.001364256       0.004092769 
      Xantusiidae 
      0.006821282 
# Create the Count  
count_habitat <- rowSums(freq_CA_short_df)
count_habitat
      Aquatic      Arboreal Bromelicolous     Fossorial  Psammophilus    Saxicolous Semi-arboreal   Terrestrial 
            7           133            10            16            10           120             6           431 
# Division Counts
CA_repartition <- (freq_CA_short_df / count_habitat) %>% 
  rbind(Average_profile = average_profile)
CA_repartition

ggplot(freq_CA_long) +
  aes(x = Family, y = Habitat, fill = Freq) + # fill says which column is used 
  # for filling
  geom_raster() + # represents in raster mode
  # and dress to make it pretty
  scale_fill_viridis_c() + # Change color scale
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

CA

# 
unique(data$Family)
 [1] Agamidae          Anguidae          Anniellidae       Carphodactylidae  Chamaeleonidae    Cordylidae        Corytophanidae    Crotaphytidae    
 [9] Dactyloidae       Diplodactylidae   Eublepharidae     Gekkonidae        Gymnophthalmidae  Helodermatidae    Hoplocercidae     Iguanidae        
[17] Lacertidae        Leiocephalidae    Leiosauridae      Liolaemidae       Opluridae         Phrynosomatidae   Phyllodactylidae  Polychrotidae    
[25] Pygopodidae       Scincidae         Shinisauridae     Sphaerodactylidae Teiidae           Tropiduridae      Varanidae         Xantusiidae      
[33] Xenosauridae      <NA>             
33 Levels: Agamidae Anguidae Anniellidae Carphodactylidae Chamaeleonidae Cordylidae Corytophanidae Crotaphytidae Dactyloidae ... Xenosauridae
result_CA <- CA(freq_CA_short_df, graph = FALSE)
fviz_eig(result_CA)

fviz_ca_biplot(result_CA, repel = TRUE)

Canonical Correspondance Analysis

Create Data

mean_latitude_df <- data_CCA %>%
  group_by(Prefered.Habitat.Type) %>%           
  summarise(mean_latitude_abs = mean(abs(Latitude), na.rm = TRUE)) %>% 
  as.data.frame()   

mean_latitude_df

Run CCA

anova(result_CCA,by="margin")
Permutation test for cca under reduced model
Marginal effects of terms
Permutation: free
Number of permutations: 999

Model: cca(formula = freq_CA_short_df ~ mean_latitude_abs, data = mean_latitude_df)
                  Df ChiSquare      F Pr(>F)
mean_latitude_abs  1   0.23944 1.2409  0.761
Residual           6   1.15773              
LS0tCnRpdGxlOiAiTGlmZSBIaXN0b3J5IFN0cmF0ZWdpZXMgaW4gTGl6YXJkczogSW5zaWdodHMgaW50bwp0aGUgU2xvdy1GYXN0IENvbnRpbnV1bSIKQXV0aG9yOiBFYW1vbiBPIENhdGhhaW4gYW5kIFJpY2hhcmQgU2xldmluCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgU2V0IFVwCiMjIEluaXRpbGlzYXRpb24KIyMjIExvYWQgTGlicmFyaWVzCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjb3JycGxvdCkgIyBGb3IgY29ycmVsYXRpb24gcGxvdHMKbGlicmFyeShGYWN0b01pbmVSKSAjIEZvciBQQ0EKbGlicmFyeShmYWN0b2V4dHJhKSAjIEZvciBQQ0EgcGxvdHMKbGlicmFyeSh2ZWdhbikgIyBGb3IgQ0NBCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyc2FtcGxlKSAjIEZvciBzYW1wbGluZyBvZiBkYXRhIChzcGxpdCwgYW5hbHlzaXMsLi4pCmxpYnJhcnkocnBhcnQpCmxpYnJhcnkocnBhcnQucGxvdCkKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShmYWN0b2V4dHJhKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGNsdXN0ZXIpCmBgYAoKIyMjIEtuaXR0aW5nIENvbmZpZ3VyYXRpb24KYGBge3J9CiMgU2V0IHRvIHJlbmRlciBjaHVua3MgZHVyaW5nIHRoZSBrbml0dGluZyBwcm9jZXNzCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgoKIyMjIFNldCBVcCB0aGUgRW52aXJvbm1lbnQKYGBge3J9CiMgQ2xlYXIgRW52aXJvbm1lbnQKcm0obGlzdCA9IGxzKCkpCgojIFNldCBXb3JraW5nIERpcmVjdG9yeQpzZXR3ZCgiL1VzZXJzL2VhbW9uL0Rlc2t0b3AvTWF0aFRvb2xzUHJvamVjdC9SX1Byb2plY3QvTWF0aGVtYXRpY2FsVG9vbHMvIikKCiMgTG9hZCBEYXRhCmRhdGEgPC0gcmVhZC5jc3YoImxpemFyZC5jc3YiKQpgYGAKCiMjIyBWaWV3IHRoZSBEYXRhCmBgYHtyfQpzdHIoZGF0YSkKYGBgCgojIyBDbGVhbmluZwojIyMgQ2hhbmdlIFZhcmlhYmxlIFR5cGVzCmBgYHtyfQojIFRoaXMgdmFyaWFibGVzIHdhcyBpbnB1dCBhcyBhIGNoYXJhY3RlciwgYnV0IGl0IHNob3VsZCBiZSBudW1lcmljCmRhdGEkU0QuRmVtYWxlLmFkdWx0LndlaWdodC4uZy4gPC0gYXMubnVtZXJpYyhkYXRhJFNELkZlbWFsZS5hZHVsdC53ZWlnaHQuLmcuKQpgYGAKCiMjIyBTZXQgQmxhbmtzIGluIENhdGVnb3JpY2FsIFZhcmlhYmxlcyBhcyBOQQpgYGB7cn0KIyBDb252ZXJ0IHRoZSBibGFuayB2YWx1ZXMgb2YgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRvIE5BCmRhdGE8LWRhdGEgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCB+IG5hX2lmKC54LCAiIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIGFzLmZhY3RvcikpCmBgYAoKIyMjIENoZWNrIGZvciBhbmQgUmVtb3ZlIER1cGxpY2F0ZXMKYGBge3J9CiMgU2F2ZSBkdXBsaWNhdGVzIGFzIGFuIG9iamVjdApkdXBsaWNhdGVzIDwtIGRhdGFbZHVwbGljYXRlZChkYXRhKSwgXQoKIyBSZW1vdmUgZHVwbGljYXRlcwpkYXRhIDwtIGRhdGFbIWR1cGxpY2F0ZWQoZGF0YSksIF0KYGBgCgojIyMgVmlzdWFsaXNlIHRoZSBQcm9wb3J0aW9uIG9mIEVhY2ggVmFyaWFibGUgV2hpY2ggaXMgTkEKQnkgZG9pbmcgdGhpcyB3ZSBmb3VuZCB0aGF0IG1vc3Qgb2YgdGhlIG51bWVyaWMgdmFyaWFibGVzIGhhZCBhIGhpZ2ggcHJvcG9ydGlvbiBvZiBOQSdzLCB3aXRoIHNvbWUgc3VjaCBhcyBSQ00gaGF2aW5nIG92ZXIgNTAlLiBUaGVyZWZvcmUgZXhjbHVzaW9uIG9mIGFsbCByb3dzIGNvbnRhaW5pbmcgTkEncyB3YXMgbGlrZWx5IHRvIGRyYXN0aWNhbGx5IHJlZHVjZSB0aGUgc2l6ZSBkYXRhc2V0IGFuZCBjYXVzZSBkYXRhIGxvc3MuIFRoZSBudW1iZXIgb2Ygcm93cyBpbiB0aGUgb3JpZ2luYWwgZGF0YXNldCB3YXMgNzM0IGFuZCB0aGUgbnVtYmVyIGFmdGVyIGV4Y2x1c2lvbiBvZiBhbGwgTkFzIHdhcyAxNDYuIFRoZXJlZm9yZSB0aGUgTkEncyB3ZXJlIHJlbW92ZWQgc2VwYXJhdGVseSBmb3IgZWFjaCBhbmFseXNpcyB1c2luZyBvbmx5IHRoZSB2YXJpYWJsZXMgcmVxdWlyZWQgZm9yIHRoYXQgYW5hbHlzaXMsIHdoaWNoIGhlbHBlZCB0byByZWR1Y2UgZGF0YSBsb3NzLgoKYGBge3J9CiMgQ2FsY3VsYXRlIHBlcmNlbnRhZ2Ugb2YgTkEgdmFsdWVzIGZvciBlYWNoIGNvbHVtbgpuYV9wZXJjZW50YWdlIDwtIGNvbFN1bXMoaXMubmEoZGF0YSkpIC8gbnJvdyhkYXRhKSAqIDEwMAoKIyBDb252ZXJ0IE5BIHBlcmNlbnRhZ2VzIHRvIGEgZGF0YSBmcmFtZQpuYV9kZiA8LSBkYXRhLmZyYW1lKAogIENvbHVtbiA9IG5hbWVzKG5hX3BlcmNlbnRhZ2UpLAogIE5BX1BlcmNlbnRhZ2UgPSBuYV9wZXJjZW50YWdlCikKCiMgQ3JlYXRlIGJhciBwbG90CmdncGxvdChuYV9kZiwgYWVzKHggPSBDb2x1bW4sIHkgPSBOQV9QZXJjZW50YWdlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInNreWJsdWUiKSArCiAgbGFicygKICAgIHRpdGxlID0gIlBlcmNlbnRhZ2Ugb2YgTkEgVmFsdWVzIHBlciBDb2x1bW4iLAogICAgeCA9ICJDb2x1bW5zIiwKICAgIHkgPSAiUGVyY2VudGFnZSAoJSkiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkgIyBSb3RhdGUgeC1heGlzIGxhYmVscwogICkKCiMgUHJpbnQgdGhlIG51bWJlciBvZiByb3dzIGluIHRoZSBmdWxsIGRhdGFzZXQKcHJpbnQocGFzdGUoIk51bWJlciBvZiByb3dzIGluIGZ1bGwgZGF0YXNldCA9IiwgbnJvdyhkYXRhKSkpCgojIFByaW50IHRoZSBudW1iZXIgb2Ygcm93cyBhZnRlciBOQSByZW1vdmFsCnByaW50KHBhc3RlKCJOdW1iZXIgb2Ygcm93cyBhZnRlciBOQSByZW1vdmFsIG9uIGVudGlyZSBkYXRhc2V0ID0iLCBucm93KG5hLm9taXQoZGF0YSkpKSkKYGBgCgojIFBDQQojIyMgQ2hvb3NlIHRoZSBWYXJpYWJsZXMgdG8gYmUgVXNlZCBpbiB0aGUgUENBClRoaXMgc2F2ZXMgdGhlIHZhcmlhYmxlcyB0byBiZSB1c2VkIGluIHRoZSBQQ0EgYXMgYW4gb2JqZWN0LiBUaGUgdmFyaWFibGVzIHVzZWQgYXJlIGVhc2lseSBhbHRlcmVkIGJ5IGNvbW1lbnRpbmcgdGhlbSBpbiBvciBvdXQuIE9ubHkgbnVtZXJpY2FsIHZhcmlhYmxlcyBhcmUgY29uc2lkZXJlZCBhcyBjYW5kaWRhdGVzIGFzIFBDQSBjYW4gb25seSB1c2UgbnVtZXJpY2FsIHZhcmlhYmxlcy4gV2hlcmUgdHdvIHZhcmlhYmxlcyBkZXNjcmliZSB0aGUgc2FtZSB0cmFpdCBhbmQvb3IgYXJlIGNvcnJlbGF0ZWQsIG9uZSBvZiB0aGVtIHdhcyByZW1vdmVkLiBUaGlzIHdhcyBkb25lIGluIG9yZGVyIHRvIGF2b2lkIGJpYXMgaW4gdGhlIFBDQSBieSBvdmVyIGVtcGhhc2lzaW5nIHBhcnRpY3VsYXIgdHJhaXRzLgoKIkYuU1ZMLmF0Lm1hdHVyaXR5Li5tbSIgZGVzY3JpYmVzIHRoZSBzYW1lIHRyYWl0IGFzICJNZWFuLkYuU1ZMLmFkdWx0cy4ubW0uIi4gQSBjb3JyZWxhdGlvbiBvZiB0aGVzZSB0d28gdmFyaWFibGVzIHJldmVhbGVkIGEgOTclIGNvcnJlbGF0aW9uLiBUaGVyZWZvcmUsICJGLlNWTC5hdC5tYXR1cml0eS4ubW0uIiB3YXMgcmFuZG9tbHkgY2hvc2VuIHRvIGJlIHJlbW92ZWQuCgpDbHV0Y2ggRnJlcXVlbmN5IGFuZCBDbHV0Y2hlcyBQZXIgWWVhciB3ZXJlIGFsc28gY29ycmVsYXRlZCBhbmQgZGVzY3JpYmUgdGhlIHNhbWUgdHJhaXQgYW5kIHRoZXJlZm9yZSBDbHV0Y2ggRnJlcXVlbmN5IHdhcyByZW1vdmVkLgoKU2FtcGxlIHNpemVzIGFuZCBzdGFuZGFyZCBkZXZpYXRpb25zIHdlcmUgcmVtb3ZlZCBmcm9tIHRoZSBhbmFseXNpcyBhcyB0aGV5IHdlcmUgbm90IHRob3VnaHQgdG8gYmUgcmVsZXZhbnQuIApgYGB7cn0KdmFyaWFibGVzX3RvX3VzZV9QQ0EgPC0gYygKICAjIyMgTG9jYXRpb24KICAjIkxvbmdpdHVkZSIsCiAgIyJMYXRpdHVkZSIsCiAgCiAgIyMjIE1lYW5zIGFuZCBOdW1lcmljYWwKICAjIkF2ZXJhZ2UuRmVtYWxlLmFkdWx0LndlaWdodC4uZy4iLAogICJNZWFuLkYuU1ZMLmFkdWx0cy4ubW0uIiwKICAjIkYuU1ZMLmF0Lm1hdHVyaXR5Li5tbS4iLAogICJPZmZzcHJpbmcuU1ZMLi5tbS4iLAogICJNZWFuLkNsdXRjaC5TaXplIiwKICAiQ2x1dGNoZXMucGVyLnllYXIiLAogICMiQ2x1dGNoLkZyZXF1ZW5jeSIsCiAgIlJDTSIKICAKICAjIyMgU3RhbmRhcmQgRGV2aWF0aW9ucyBhbmQgU2FtcGxlIFNpemVzCiAgIyJTRC5GZW1hbGUuYWR1bHQud2VpZ2h0Li5nLiIsCiAgIyJTRC5GLlNWTC5hZHVsdHMuLm1tLiIsCiAgIyJTYW1wbGUuU2l6ZS5GZW1hbGUuYWR1bHQud2VpZ2h0IiwKICAjIlNhbXBsZS5zaXplLk1lYW4uRi5TVkwuYWR1bHRzIiwKKQpgYGAKCiMjIyBJbnZlc3RpZ2F0aW9uIG9mIHRoZSBTaW1pbGFyIFZhcmlhYmxlcwpUbyBpbnZlc3RpZ2F0ZSB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gdmFyaWFibGVzLgpgYGB7cn0KZGF0YSAlPiUgCiAgc2VsZWN0KHdoZXJlKGlzLm51bWVyaWMpLCAtTGF0aXR1ZGUsIC1Mb25naXR1ZGUsIC1TRC5GZW1hbGUuYWR1bHQud2VpZ2h0Li5nLiwgLVNELkYuU1ZMLmFkdWx0cy4ubW0uLCAtU2FtcGxlLlNpemUuRmVtYWxlLmFkdWx0LndlaWdodCwgLVNhbXBsZS5zaXplLk1lYW4uRi5TVkwuYWR1bHRzLCAtU2FtcGxlLnNpemUuQ2x1dGNoLlNpemUuKSAlPiUKICBjb3IodXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKSAlPiUgIyBDYWxjdWxhdGUgdGhlIGVtcGlyaWNhbCBjb3JyZWxhdGlvbiBtYXRyaXgKICBjb3JycGxvdCh0bC5jZXggPSAwLjcpICMgVGhlbiBncmFwaCB0aGlzIG1hdHJpeApgYGAKCiMjIyBDcmVhdGUgTmV3IERhdGFzZXQgcmV0YWluaW5nIG9ubHkgdGhlIFBDQSB2YXJpYWJsZXMKSGVyZSBhIG5ldyBzdWJzZXQgb2YgdGhlIGRhdGEgaXMgY3JlYXRlZCBmb3IgdGhlIFBDQS4gVGhlIFBDQSBkYXRhIHdhcyBjbGVhbmVkIG9mIGFsbCByb3dzIGNvbnRhaW5pbmcgTkEncyBhbmQgc3RhbmRhcmRpc2VkIHN1Y2ggdGhhdCBlYWNoIHZhcmlhYmxlIGhhcyBtZWFuIDAgYW5kIHZhcmlhbmNlIDEgKHdoaWNoIGFsbG93cyBmb3IgY29tcGFyaXNvbiBvZiB2YXJpYWJsZXMgb24gZGlmZmVyZW50IHNjYWxlcyBpbiB0aGUgUENBKS4gQSBzZWNvbmQgZGF0YSBzZXQgd2FzIGNyZWF0ZWQgY29udGFpbmluZyB0aGUgcm93cyByZXRhaW5lZCBhZnRlciBOQSBvbWl0aW9uIG9mIHRoZSBQQ0EgZGF0YSwgYnV0IGNvbnRhaW5pbmcgYWxsIHRoZSBvcmlnaW5hbCB2YXJpYWJsZXMuIFRoaXMgaXMgbGF0ZXIgdXNlZCB0byBjb2xvciB0aGUgUENBIHZpc3VhbGlzYXRpb25zIHdpdGggY2F0ZWdvcmljYWwgdmFyaWFibGVzLgoKYGBge3J9CiMgQ3JlYXRlIHRoZSBuZXcgUENBIGRhdGFzZXQKZGF0YV9QQ0EgPC0gZGF0YSAlPiUKICBzZWxlY3QoYW55X29mKHZhcmlhYmxlc190b191c2VfUENBKSkgJT4lICMgZmlsdGVyIHZhcmlhYmxlcwogIG5hLm9taXQoKSAlPiUgIyBvbWl0IE5BJ3MKICBtdXRhdGVfYWxsKC5mdW5zID0gc2NhbGUpICU+JSAjIHN0YW5kYXJkaXNlIHRoZSBkYXRhCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJyb3dfaWQiKSAjIHNldCByb3duYW1lcyBhcyBhIGNvbHVtbgoKIyBDcmVhdGUgYW5jaWxsYXJ5IGRhdGFzZXQgd2l0aCBhbGwgb3JpZ2luYWwgdmFyaWFibGVzIGJ1dCBmaWx0ZXJlZCB0byBrZWVwIG9ubHkgcm93cyBwcmVzZW50IGluIHRoZSBQQ0EgZGF0YXNldAojIFRoaXMgd2lsbCBiZSB1c2VkIGZvciBjb2xvcmF0aW9uIG9mIHRoZSBQQ0EgZ3JhcGhzIHdpdGggY2F0ZWdvcmljYWwgdmFyaWFibGVzCgpkYXRhX1BDQV9hbGxfdmFycyA8LSBkYXRhICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAicm93X2lkIikgJT4lICMgc2V0IHJvd25hbWVzIGFzIGEgY29sdW1uCiAgZmlsdGVyKHJvd19pZCAlaW4lIGRhdGFfUENBJHJvd19pZCkgJT4lICMgZmlsdGVyIHRoZSBkYXRhIHRvIG1hdGNoIHJvd3Mgb2YgZGF0YV9QQ0EKICBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gInJvd19pZCIpICMgQ29udmVydCBjb2x1bW4gYmFjayB0byByb3duYW1lcwoKIyBDb252ZXJ0IHRoZSBjb2x1bW4gYmFjayB0byByb3duYW1lcyBmb3IgdGhlIFBDQSBkYXRhCmRhdGFfUENBIDwtIGRhdGFfUENBICU+JQogIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAicm93X2lkIikKYGBgCgojIyMgVmlzdWFsaXNlIHdpdGggYSBDb3JyZWxhdGlvbgpUaGlzIHNob3dzIHRoZSBjb3JyZWxhdGlvbiBvZiBlYWNoIHZhcmlhYmxlIHBhaXJzIGluIGZpbHRlcmVkIFBDQSBkYXRhLiBOb25lIG9mIHRoZSB2YXJpYWJsZXMgYXJlIGhpZ2hseSBjb3JyZWxhdGVkIHdpdGggZWFjaCBvdGhlci4KYGBge3J9CiMjIyBDb3JyUGxvdApkYXRhX1BDQSAlPiUgCiAgY29yKHVzZT0icGFpcndpc2UuY29tcGxldGUub2JzIikgJT4lICMgQ2FsY3VsYXRlIHRoZSBlbXBpcmljYWwgY29ycmVsYXRpb24gbWF0cml4CiAgY29ycnBsb3QoKSAjIFRoZW4gZ3JhcGggdGhpcyBtYXRyaXgKYGBgCgojIyMgUnVuIHRoZSBQQ0EKSGVyZSB0aGUgUENBIGlzIHJ1biBhbmQgdGhlIHJlc3VsdHMgYXJlIHByZXNlbnRlZCBpbiB2YXJpb3VzIGZvcm1zIGluY2x1ZGluZyB0aGUgb3V0cHV0IHRhYmxlcywgYSBzY3JlZSBwbG90IHNob3dpbmcgdGhlIHBlcmNlbnRhZ2Ugb2YgZXhwbGFpbmVkIHZhcmlhbmNlIGJ5IGVhY2ggcHJpbmNpcGxlIGNvbXBvbmVudCBhbmQgdGhlIGNvcnJlbGF0aW9uIG9mIGVhY2ggdmFyaWFibGUgd2l0aCBlYWNoIHByaW5jaXBsZSBjb21wb25lbnQuCmBgYHtyfQojIFJ1biB0aGUgUENBCnJlc3VsdF9wY2EgPC0gUENBKGRhdGFfUENBLCAKICAgICAgICAgICAgICAgc2NhbGUudW5pdCA9IFRSVUUsICMgT3B0aW9uIHRvIGNlbnRlciBhbmQgc2NhbGUgZGF0YSAodXNlbGVzcyBoZXJlKQogICAgICAgICAgICAgICBuY3AgPSAxOCwgIyBOdW1iZXIgb2YgY29tcG9uZW50cyB0byBrZWVwIChoZXJlLCBhbGwpCiAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UpCgojIFJlc3VsdHMgb2YgdGhlIFBDQQpyZXN1bHRfcGNhJGVpZwpyZXN1bHRfcGNhJHZhcgoKIyBQbG90IHRoZSBTY3JlZSBQbG90IHRvIFNob3cgVmFyaWFuY2UgRXhwbGFpbmVkIGJ5IEVhY2ggRGltZW5zaW9uCmZ2aXpfZWlnKHJlc3VsdF9wY2EsIGNob2ljZSA9ICJ2YXJpYW5jZSIpCgojIFBsb3QgdGhlIENvcnJlbGF0aW9uIG9mIEVhY2ggVmFyaWFibGUgYW5kIHRoZSBEaW1lbnNpb24KY29ycl9wbG90X3BjYSA8LWNvcnJwbG90KHJlc3VsdF9wY2EkdmFyJGNvcikKY29ycl9wbG90X3BjYQpgYGAKCiMjIyBQbG90IHRoZSBJbmRpdmlkdWFscyB3aXRoIHRoZSBWYXJpYWJsZSBBcnJvd3MgYW5kIHRoZSBDYXRlZ29yaWNhbCBDb2xvcmluZwpCeSBhbHRlcmluZyB0aGUgb2JqZWN0IENvbG9yX2xhYmVsIHdlIHdlcmUgYWJsZSB0byBjaGVjayB0aGUgZWZmZWN0IG9mIGNvbG9yaW5nIHdpdGggZWFjaCBvZiB0aGUgZm91ciBtYWluIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyAoRGlzdHJpYnV0aW9uLCBIYWJpdGF0IHR5cGUsIEZvcmFnaW5nIG1vZGUgYW5kIFJlcHJvZHVjdGl2ZSB0eXBlKS4gSXQgd2FzIGZvdW5kIHRoYXQgZGlzdHJpYnV0aW9uIHByb2R1Y2VkIHRoZSBjbGVhcmVzdCBjbHVzdGVyaW5nIHdpdGggdGhlIGxlYXN0IG5vaXNlLiBUaGVyZWZvcmUgZGlzdHJpYnV0aW9uIHdhcyB0YWtlbiBmb3J3YXJkIGFzIGEgcG90ZW50aWFsIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHdoaWNoIGNhbiBhY2NvdW50IGZvciBtdWNoIG9mIHRoZSB2YXJpYXRpb24uIFRoaXMgY29kZSBjb3VsZCBvZiBiZWVuIHNob3J0ZW5lZCB1c2luZyBhIGxvb3AsIGhvd2V2ZXIgaXQgYWZmZWN0ZWQgdGhlIHNjYWxpbmcgb2YgdGhlIGF4ZXMgYW5kIHRoZSBmb250IHNpemUgYW5kIHdlIHdlcmUgdW5hYmxlIHRvIHJlc29sdmUgdGhpcyBpc3N1ZS4KCmBgYHtyfQojIERlZmluZSBHcmFwaGluZyBGdW5jdGlvbgpwbG90X3BjYV93aXRoX2NvbG9yIDwtIGZ1bmN0aW9uKGNvbG9yX2xhYmVsKSB7CiAgcDEgPC0gZnZpel9wY2FfYmlwbG90KHJlc3VsdF9wY2EsIGF4ZXMgPSBjKDEsIDIpLCBjb2wuaW5kID0gZGF0YV9QQ0FfYWxsX3ZhcnNbW2NvbG9yX2xhYmVsXV0sIGNvbC52YXIgPSAiYmxhY2siKQogIHAyIDwtIGZ2aXpfcGNhX2JpcGxvdChyZXN1bHRfcGNhLCBheGVzID0gYygxLCAzKSwgY29sLmluZCA9IGRhdGFfUENBX2FsbF92YXJzW1tjb2xvcl9sYWJlbF1dLCBjb2wudmFyID0gImJsYWNrIikKICBwMyA8LSBmdml6X3BjYV9iaXBsb3QocmVzdWx0X3BjYSwgYXhlcyA9IGMoMiwgMyksIGNvbC5pbmQgPSBkYXRhX1BDQV9hbGxfdmFyc1tbY29sb3JfbGFiZWxdXSwgY29sLnZhciA9ICJibGFjayIpCiAgcDQgPC0gZnZpel9wY2FfYmlwbG90KHJlc3VsdF9wY2EsIGF4ZXMgPSBjKDMsIDQpLCBjb2wuaW5kID0gZGF0YV9QQ0FfYWxsX3ZhcnNbW2NvbG9yX2xhYmVsXV0sIGNvbC52YXIgPSAiYmxhY2siKQogIGdyaWQuYXJyYW5nZShwMSwgcDIsIHAzLCBwNCwgbnJvdyA9IDIsIG5jb2wgPSAyKQp9CgojIFVzZSBmdW5jdGlvbiB3aXRoIERpc3RyaWJ1dGlvbgpkaXN0X3Bsb3QgPC0gcGxvdF9wY2Ffd2l0aF9jb2xvcigiRGlzdHJpYnV0aW9uIikKIyBTYXZlIFBsb3QKZ2dzYXZlKCJQQ0FfcGxvdF9kaXN0cmlidXRpb24ucG5nIiwgcGxvdCA9IGRpc3RfcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOC40LCB1bml0cyA9ICJpbiIsIGRwaSA9IDUwMCkKIyBQcmludCBwbG90CmRpc3RfcGxvdAoKIyBQbG90IGZvciBvdGhlciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMKZGlzdF9wbG90IDwtIHBsb3RfcGNhX3dpdGhfY29sb3IoIlByZWZlcmVkLkhhYml0YXQuVHlwZSIpCmRpc3RfcGxvdCA8LSBwbG90X3BjYV93aXRoX2NvbG9yKCJNb2RlLm9mLnJlcHJvZHVjdGlvbiIpCmRpc3RfcGxvdCA8LSBwbG90X3BjYV93aXRoX2NvbG9yKCJGb3JhZ2luZy5Nb2RlIikKYGBgCgojIEstTWVhbnMgQ2x1c3RlcmluZwojIyMgQ3JlYXRlIENsdXN0ZXJzIGZvciAyLTUgR3JvdXBzIE91dCBvZiBDdXJpb3NpdHkKYGBge3J9CiMgQ3JlYXRlIGNsdXN0ZXJzIGZvciAyIEdyb3VwcyAtIFRvIE1hdGNoIERpc3RyaWJ1dGlvbiBMZXZlbHMKa21lYW5zXzJncm91cHMgPC0gZGF0YV9QQ0EgJT4lCiAga21lYW5zKGNlbnRlcnM9MiwgaXRlci5tYXggPSAxMDAwKQoKIyBDYWxjdWxhdGUgdGhlIHdpdGhpbiBncm91cCBpbmVydGlhCiMgV2l0aGluIEdyb3VwIEluZXJ0aWEKa21lYW5zXzJncm91cHMkdG90c3MKa21lYW5zXzJncm91cHMkdG90LndpdGhpbnNzCmttZWFuc18yZ3JvdXBzJHdpdGhpbnNzCmttZWFuc18yZ3JvdXBzJGJldHdlZW5zcwojIENyZWF0ZSBDbHVzdGVycyBGb3IgTW9yZSBHcm91cHMgT3V0IEN1cmlvc2l0eQpmaXJzdF9rbWVhbnMgPC0gZGF0YV9QQ0EgJT4lIAogIGttZWFucyhjZW50ZXJzID0gNCwgaXRlci5tYXggPSAxMDAwKQoKa21lYW5zXzVncm91cHM8LSBkYXRhX1BDQSAlPiUKICBrbWVhbnMoY2VudGVycyA9IDUsIGl0ZXIubWF4ID0gMTAwMCkKCmttZWFuc180Z3JvdXBzPC0gZGF0YV9QQ0EgJT4lCiAga21lYW5zKGNlbnRlcnMgPSA0LCBpdGVyLm1heCA9IDEwMDApCgprbWVhbnNfM2dyb3VwczwtIGRhdGFfUENBICU+JQogIGttZWFucyhjZW50ZXJzPTMsIGl0ZXIubWF4ID0gMTAwMCkKCiMgRXh0cmFjdCB0aGUgQ2x1c3RlcmluZyBHcm91cHMKY2x1c3RlcnNfNWdyb3VwcyA8LSBrbWVhbnNfNWdyb3VwcyRjbHVzdGVyCmNsdXN0ZXJzXzRncm91cHMgPC0ga21lYW5zXzRncm91cHMkY2x1c3RlcgpjbHVzdGVyc18zZ3JvdXBzIDwtIGttZWFuc18zZ3JvdXBzJGNsdXN0ZXIKY2x1c3RlcnNfMmdyb3VwcyA8LSBrbWVhbnNfMmdyb3VwcyRjbHVzdGVyCmBgYAoKIyMjIFBsb3QgYSBDb21wYXJpc29uIG9mIHRoZSAyIEdyb3VwIEstTWVhbnMgQ2x1c3RlcmluZyB2cyBUaGUgQ29sb3JhdGlvbiBBcHBsaWVkIGJ5IHRoZSAiRGlzdHJpYnV0aW9uIiBWYXJpYWJsZQpUaGUgay1tZWFucyBtZXRob2QgZm91bmQgY2x1c3RlcnMgd2hpY2ggdmlzdWFsbHkgc2VlbSB2ZXJ5IHNpbWlsYXIgdG8gdGhvc2UgYXBwbGllZCBieSB0aGUgRGlzdHJpYnV0aW9uIGNhdGVnb3J5LgpgYGB7cn0KQ29sb3JfbGFiZWwgPSAiRGlzdHJpYnV0aW9uIgoKIyBEZWZpbmUgYSBjb2xvciBwYWxldHRlIGZvciBjb25zaXN0ZW5jeQpjdXN0b21fcGFsZXR0ZSA8LSBjKCJyZWQiLCAiYmx1ZSIsICJncmVlbiIsICJvcmFuZ2UiKQoKIyBTYXZlIGVhY2ggb2YgdGhlIHBsb3RzIApwMUEgPC0gZnZpel9wY2FfYmlwbG90KHJlc3VsdF9wY2EsIGF4ZXMgPSBjKDEsIDIpLCBjb2wuaW5kID0gYXMuZmFjdG9yKGNsdXN0ZXJzXzJncm91cHMpLCBjb2wudmFyID0gImJsYWNrIiwgdGl0bGUgPSAiUEMxICYgUEMyIC0gQ2x1c3RlcmluZyIpICsgeWxpbSgtNCwgNCkKcDFCIDwtIGZ2aXpfcGNhX2JpcGxvdChyZXN1bHRfcGNhLCBheGVzID0gYygxLCAyKSwgY29sLmluZCA9IGRhdGFfUENBX2FsbF92YXJzW1tDb2xvcl9sYWJlbF1dLCBjb2wudmFyID0gImJsYWNrIiwgdGl0bGUgPSAiUEMxICYgUEMyIC0gRGlzdHJpYnV0aW9uIikrIHlsaW0oLTQsIDQpIApwMkEgPC0gZnZpel9wY2FfYmlwbG90KHJlc3VsdF9wY2EsIGF4ZXMgPSBjKDEsIDMpLCBjb2wuaW5kID0gYXMuZmFjdG9yKGNsdXN0ZXJzXzJncm91cHMpLCBjb2wudmFyID0gImJsYWNrIiwgdGl0bGUgPSAiUEMxICYgUEMzIC0gSy1NZWFucyBDbHVzdGVyaW5nIikrIHlsaW0oLTQsIDQpIApwMkIgPC0gZnZpel9wY2FfYmlwbG90KHJlc3VsdF9wY2EsIGF4ZXMgPSBjKDEsIDMpLCBjb2wuaW5kID0gZGF0YV9QQ0FfYWxsX3ZhcnNbW0NvbG9yX2xhYmVsXV0sIGNvbC52YXIgPSAiYmxhY2siLCB0aXRsZSA9ICJQQzEgJiBQQzMgLSBEaXN0cmlidXRpb24iKSsgeWxpbSgtNCwgNCkKcDNBIDwtIGZ2aXpfcGNhX2JpcGxvdChyZXN1bHRfcGNhLCBheGVzID0gYygyLCAzKSwgY29sLmluZCA9IGFzLmZhY3RvcihjbHVzdGVyc18yZ3JvdXBzKSwgY29sLnZhciA9ICJibGFjayIsIHRpdGxlID0gIlBDMiAmIFBDMyAtIEstTWVhbnMgQ2x1c3RlcmluZyIpKyB5bGltKC00LCA0KQpwM0IgPC0gZnZpel9wY2FfYmlwbG90KHJlc3VsdF9wY2EsIGF4ZXMgPSBjKDIsIDMpLCBjb2wuaW5kID0gZGF0YV9QQ0FfYWxsX3ZhcnNbW0NvbG9yX2xhYmVsXV0sIGNvbC52YXIgPSAiYmxhY2siLCB0aXRsZSA9ICJQQzIgJiBQQzMgLSBEaXN0cmlidXRpb24iKSt5bGltKC00LCA0KQoKIyBQbG90IGVhY2ggYXhpcyBjb21iaW5hdGlvbgphbGxfNF9wbG90czwtZ3JpZC5hcnJhbmdlKHAxQSwgcDFCLCBwMkEsIHAyQiwgcDNBLCBwM0IsIG5yb3cgPSA0LCBuY29sPTIpCgojIFNhdmUgYSBnZ3Bsb3Qgb2JqZWN0Cmdnc2F2ZSgiQ2x1c3RlcmluZy1LbWVhbnMucG5nIiwgcGxvdCA9IGFsbF80X3Bsb3RzLCB3aWR0aCA9IDExLCBoZWlnaHQgPSAxMi4xOCwgdW5pdHMgPSAiaW4iLCBkcGkgPSA1MDApCmBgYAojIENvbXBhcmlzb24gb2YgSy1NZWFucyBDbHVzdGVyaW5ncyBhbmQgRGlzdHJpYnV0aW9uIEdyb3VwaW5ncyBVc2luZyBDaGktU3F1YXJlZCBUZXN0IG9mIEluZGVwZW5kZW5jZQpgYGB7cn0KIyBDb252ZXJ0IGttZWFucyBvdXRwdXQgdG8gYSBkYXRhZnJhbWUKZGF0YV93aXRoX2NsdXN0ZXJzIDwtIGRhdGFfUENBICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUgICAgICAgICAgICAgICAgICAjIEVuc3VyZSBpdCdzIGEgZGF0YWZyYW1lCiAgbXV0YXRlKENsdXN0ZXIgPSBrbWVhbnNfMmdyb3VwcyRjbHVzdGVyKSAlPiUgIyBBZGQgdGhlIGNsdXN0ZXIgYXNzaWdubWVudHMKICBtdXRhdGUoRGlzdHJpYnV0aW9uID0gZGF0YV9QQ0FfYWxsX3ZhcnMkRGlzdHJpYnV0aW9uKQpkYXRhX3dpdGhfY2x1c3RlcnMKCiMgQ29udGluZ2VuY3kgdGFibGUKdGFibGVfa21lYW5zX3ZzX2Rpc3RyaWJ1dGlvbiA8LSB0YWJsZShrbWVhbnNfY2x1c3RlcnMgPSBkYXRhX3dpdGhfY2x1c3RlcnMkQ2x1c3RlciwgZGlzdHJpYnV0aW9uID0gZGF0YV93aXRoX2NsdXN0ZXJzJERpc3RyaWJ1dGlvbikKCiMgUHJpbnQgdGhlIHRhYmxlCnByaW50KHRhYmxlX2ttZWFuc192c19kaXN0cmlidXRpb24pCgojIFBlcmZvcm0gY2hpLXNxdWFyZSB0ZXN0CmNoaXNxX3Rlc3QgPC0gY2hpc3EudGVzdCh0YWJsZV9rbWVhbnNfdnNfZGlzdHJpYnV0aW9uKQpwcmludChjaGlzcV90ZXN0KQpgYGAKCgojIFVuc3VwZXJ2aXNlZCBMZWFybmluZwojI1NwbGl0IERhdGEKIyMjIFNwZWNpZnkgVmFyaWFibGVzIHRvIFVzZQpgYGB7cn0KIyBTcGVjaWZ5IHRoZSBWYXJpYWJsZXMgdG8gVXNlIGluIHRoZSBSYW5kb20gRm9yZXN0cyBBbmFseXNpcwp2YXJpYWJsZXNfdG9fdXNlX1JGIDwtIGMoCiAgIyMjIENhdGVnb3JpY2FsIFZhcmlhYmxlcwogICJNb2RlLm9mLnJlcHJvZHVjdGlvbiIsCiAgIkRpc3RyaWJ1dGlvbiIsCiAgIlByZWZlcmVkLkhhYml0YXQuVHlwZSIsCiAgIkZvcmFnaW5nLk1vZGUiLAogIAogICMjIyBMb2NhdGlvbgogICMiTG9uZ2l0dWRlIiwKICAjIkxhdGl0dWRlIiwKICAKICAjIyMgTnVtZXJpY2FsIFZhcmlhYmxlcwogICMiQXZlcmFnZS5GZW1hbGUuYWR1bHQud2VpZ2h0Li5nLiIsCiAgIk1lYW4uRi5TVkwuYWR1bHRzLi5tbS4iLAogICMiRi5TVkwuYXQubWF0dXJpdHkuLm1tLiIsCiAgIk9mZnNwcmluZy5TVkwuLm1tLiIsCiAgIk1lYW4uQ2x1dGNoLlNpemUiLAogICJDbHV0Y2hlcy5wZXIueWVhciIsCiAgIyJDbHV0Y2guRnJlcXVlbmN5IiwKICAiUkNNIgogIAogICMjIyBTdGFuZGFyZCBEZXZpYXRpb25zIGFuZCBTYW1wbGUgU2l6ZXMKICAjIlNELkYuU1ZMLmFkdWx0cy4ubW0uIiwKICAjIlNhbXBsZS5zaXplLk1lYW4uRi5TVkwuYWR1bHRzIiwKICAjIlNELkZlbWFsZS5hZHVsdC53ZWlnaHQuLmcuIiwKICAjIlNhbXBsZS5TaXplLkZlbWFsZS5hZHVsdC53ZWlnaHQiCikKYGBgCgoKIyMjRmlsdGVyIERhdGEgVmFyaWFibGVzCkZpbHRlciBvdXQgdGhlIGRhdGEgdG8gb25seSBrZWVwIHZhcmlhYmxlcyB0byBiZSB1c2VkIGluIHRoZSByYW5kb20gZm9yZXN0cyBhbmQgS05OIGFuYWx5c2VzLgpSZW1vdmUgTmFzLgpgYGB7cn0KZGF0YV9SRiA8LSBkYXRhICU+JQogIHNlbGVjdChhbnlfb2YodmFyaWFibGVzX3RvX3VzZV9SRikpICU+JSAjIFNlbGVjdCB2YXJpYWJsZXMKICBuYS5vbWl0KCkgJT4lCiAgZHJvcGxldmVscygpCmBgYAoKIyMjIEluaXRpYWwgU3BsaXQKU3BsaXQgdGhlIGRhdGEgc3VjaCB0aGF0IDgwJSBpcyB1c2VkIGZvciB0aGUgdHJhaW5pbmcgYW5kIDIwJSBpcyB1bnNlZW4uCmBgYHtyfQpzcGxpdF9kYXRhIDwtIGRhdGFfUkYgJT4lCiAgaW5pdGlhbF9zcGxpdChwcm9wID0gMC44LCBzdHJhdGEgPSAiRGlzdHJpYnV0aW9uIikKCiMgVHJhaW4gYW5kIFRlc3QgRGF0YQp0cmFpbl9kYXRhIDwtIGFuYWx5c2lzKHNwbGl0X2RhdGEpCnRlc3RfZGF0YSA8LSBhc3Nlc3NtZW50KHNwbGl0X2RhdGEpCmBgYAoKIyMgUmFuZG9tIEZvcmVzdHMKIyMjIENob29zZSB0aGUgT3B0aW11bSBIeXBlcnBhcmFtZXRlciAnbXRyeScuClRoaXMgY2h1bmsgZW5hYmxlcyB1cyB0byBkZXRlcm1pbmUgdGhlIG9wdGltdW0gdmFsdWUgb2YgdGhlIGh5cGVycGFyYW1ldGVyIG10cnkuIEhlcmUgaXQgaXMgZm91bmQgdG8gYmUgYXQgdGhlIGhpZ2hlc3QgZm9yIHRoZSBtdHJ5IHZhbHVlcyA0LCA1IGFuZCA2LgpgYGB7cn0KIyBJbml0aWFsaXNhdGlvbgpwYXJhbXNfQ1YgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gMTAsIAogICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGVhdHMgPSA1KQoKcHJlZGljdG9yX2ZvcmVzdCA8LSBjYXJldDo6dHJhaW4oRGlzdHJpYnV0aW9uIH4gLiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX1JGLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJmIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldHJpYyA9ICJBY2N1cmFjeSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBwYXJhbXNfQ1YsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKG10cnkgPSAyOjEyKSkKCmFzLmRhdGEuZnJhbWUocHJlZGljdG9yX2ZvcmVzdCRyZXN1bHRzKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gbXRyeSwgeSA9IEFjY3VyYWN5KSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCiMjIyBDcmVhdGUgdGhlIFJGClVzZSA1MDAwIHJhbmRvbSB0cmVlcyBhbmQgbXRyeSB2YWx1ZSAzLgpgYGB7cn0KZm9yZXN0IDwtIHJhbmRvbUZvcmVzdChEaXN0cmlidXRpb24gfiAuLCAjIEZvcm11bGEgZm9yIHByZWRpY3Rpb24KICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbl9kYXRhLCAjIERhdGEgZm9yIHRyYWluaW5nCiAgICAgICAgICAgICAgICAgICAgICBudHJlZSA9IDUwMDAsICMgTnVtYmVyIG9mIHRyZWVzCiAgICAgICAgICAgICAgICAgICAgICBtYXhub2RlcyA9IDEwLCAjIE51bWJlciBvZiBtYXhpbXVtIGxlYXZlcyBmb3IgZWFjaCB0cmVlCiAgICAgICAgICAgICAgICAgICAgICBtdHJ5ID0gMywgIyBOdW1iZXIgb2YgdmFyaWFibGVzIGZvciBlYWNoIHRyZWUKICAgICAgICAgICAgICAgICAgICAgIGltcG9ydGFuY2UgPSBUUlVFKSAjIENvbXB1dGF0aW9uIG9mIGltcG9ydGFuY2UKYGBgCgojIyMgVGVzdCB0aGUgQWNjdXJhY3kKQWNjdXJhY3kgZm91bmQgdG8gYmUgMC44MjkKYGBge3J9CiMgRmFzdCB3YXkgb2YgY29tcHV0aW5nIGFjY3VyYWN5IHdpdGggdGhlIHBpcGUgb3BlcmF0b3IgJT4lIApjb25mdXNpb25fbWF0cml4IDwtcHJlZGljdChmb3Jlc3QsIG5ld2RhdGEgPSBzZWxlY3QodGVzdF9kYXRhLCAtIERpc3RyaWJ1dGlvbikpICU+JSAKICB0YWJsZShwcmVkaWN0aW9uID0gLiwgdHJ1dGggPSB0ZXN0X2RhdGEkRGlzdHJpYnV0aW9uKSAKCmFjY3VyYWN5IDwtIGNvbmZ1c2lvbl9tYXRyaXggJT4lCiAge3N1bShkaWFnKC4pKSAvIHN1bSguKX0KCmNvbmZ1c2lvbl9tYXRyaXgKYWNjdXJhY3kKYGBgCgojIyMgRmluZCB0aGUgVmFyaWFibGUgSW1wb3J0YW5jZQpGb3VuZCB0aGUgTWVhbiBDbHV0Y2ggU2l6ZSBjYXVzZWQgdGhlIGhpZ2hlc3QgbWVhbiBkZWNyZWFzZSBpbiBhY2N1cmFjeSBhbmQgbWVhbiBkZWNyZWFzZSBpbiB0aGUgR2luaSBjb2VmZmljaWVudCBvZiBpbXB1cml0eS4gVGhlcmVmb3JlIGl0IGlzIHRoZSB2YXJpYWJsZSBjb250cmlidXRpbmcgdGhlIG1vc3QgdG8gdGhlIGNsYXNzaWZpY2F0aW9uLiAKCmBgYHtyfQpwbmcoIlZhcmlhYmxlX2ltcG9ydGFuY2UucG5nIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gOCwgdW5pdHMgPSAiaW4iLCByZXMgPSA1MDApCnZhckltcFBsb3QoCiAgZm9yZXN0LCAKICBtYWluID0gIlZhcmlhYmxlIEltcG9ydGFuY2UgUGxvdCBSYW5kb20gRm9yZXN0cyIsCiAgY2V4ID0gMC43LAogIAopCmRldi5vZmYoKQoKdmFySW1wUGxvdCgKICBmb3Jlc3QsIAogIG1haW4gPSAiVmFyaWFibGUgSW1wb3J0YW5jZSBQbG90IFJhbmRvbSBGb3Jlc3RzIiwKICBjZXggPSAwLjcgICMgQWRqdXN0IGZvbnQgc2l6ZQopCmBgYAoKIyMgS05OIAojIyMgU3BsaXQgdGhlIERhdGEgUmFuZG9tbHkKYGBge3J9CiMgV2Ugc3BsaXQgdGhlIGRhdGEgKDgwJSBmb3IgdHJhaW5pbmcpCmZpcnN0X3NwbGl0IDwtIGluaXRpYWxfc3BsaXQoZGF0YV9SRiwgcHJvcCA9IDAuOCkKZmlyc3RfdHJhaW5fc2V0IDwtIGFuYWx5c2lzKGZpcnN0X3NwbGl0KSAKZGltKGZpcnN0X3RyYWluX3NldCk7IGhlYWQoZmlyc3RfdHJhaW5fc2V0KQpmaXJzdF90ZXN0X3NldCA8LSBhc3Nlc3NtZW50KGZpcnN0X3NwbGl0KSAKZGltKGZpcnN0X3Rlc3Rfc2V0KTsgaGVhZChmaXJzdF90ZXN0X3NldCkKYGBgCgojIEFwcGVuZGl4IC0gQ0EgYW5kIENDQQpUaGlzIHNlY3Rpb24gaXMgbm90IHByZXNlbnRlZCBpbiB0aGUgbWFpbiB0ZXh0LiBXZSB0cmllZCB0byBkbyBhIENBIGFuZCBDQ0EgYnV0IHRoZSBsYWNrIG9mIGVudmlyb25tZW50YWwgdmFyaWFibGVzIHByb3ZlZCB0byBiZSBhbiBvYnN0YWNsZS4gQWJzb2x1dGUgbGF0aXR1ZGUgd2FzIGF2ZXJhZ2VkIGZvciBlYWNoIGhhYml0YXQgaW4gb3JkZXIgdG8gdGVzdCB0aGUgZWZmZWN0IG9mIGxhdGl0dWRlL2NsaW1hdGUgb24gdGhlIGFzc29jaWF0aW9uIGJldHdlZW4gZmFtaWxpZXMgYW5kIHBhcnRpY3VsYXIgaGFiaXRhdHMuIEhvd2V2ZXIgbm8gYXNzb2NpYXRpb24gd2FzIGZvdW5kIGFuZCB3ZSB3ZXJlIHVuc3VyZSBpZiBhdmVyYWdpbmcgdGhlIHZhbHVlIG9mIGxhdGl0dWRlcyBmb3IgdGhlIGhhYml0YXRzIHdhcyBhcHByb3ByaWF0ZS4gIAoKIyBWYXJpYWJsZXMgdG8gdXNlIGluIHRoZSBBbmFseXNpcwpgYGB7cn0KdmFyaWFibGVzX3RvX3VzZV9DQ0EgPC0gYygKICAjIkRpc3RyaWJ1dGlvbiIsCiAgIyJGb3JhZ2luZy5Nb2RlIiwKICAjIk1vZGUub2YucmVwcm9kdWN0aW9uIiwKICAiUHJlZmVyZWQuSGFiaXRhdC5UeXBlIiwKICAiRmFtaWx5IiwKICAiTGF0aXR1ZGUiCikKYGBgCgoKIyMjIEludGlpYWwgRGF0YSBXcmFuZ2xpbmcgCmBgYHtyfQojIFNlbGVjdCBhbGwgZm91ciB2YXJpYWJsZXMgdG8gYmUgdXNlZCBpbiB0aGUgQ0EgYW5kIENDQSwgb21pdCBOQXMKZGF0YV9DQ0EgPC0gZGF0YSAlPiUKICAjIFNlbGVjdCBvbmx5IHRoZSB2YXJpYWJsZXMgdG8gdXNlIGZvciB0aGUgQ0EgYW5kIENDQQogIHNlbGVjdChhbnlfb2YodmFyaWFibGVzX3RvX3VzZV9DQ0EpKSAlPiUKICAjIEV4Y2x1ZGUgTkFzCiAgbmEub21pdCgpIAoKIyBNYWtlIGEgZnJlcXVlbmN5IHRhYmxlIGNvbnRhaW5pbmcganVzdCAyIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBmb3IgdGhlIENBCiMgV2l0aCBlYWNoIHBhaXIgb2YgbGV2ZWxzIG9uIGEgc2VwZXJhdGUgcm93CmZyZXFfQ0FfbG9uZyA8LSBkYXRhX0NDQSAlPiUKICBzZWxlY3QoRmFtaWx5LCBQcmVmZXJlZC5IYWJpdGF0LlR5cGUpICU+JQogIHRhYmxlKCkgJT4lCiAgYXNfdGliYmxlICU+JQogIHJlbmFtZShGcmVxID0gIm4iLAogICAgICAgICBIYWJpdGF0ID0gIlByZWZlcmVkLkhhYml0YXQuVHlwZSIpCgojIE1ha2UgYSB0aWJibGUgd2l0aCB0aGUgY291bnQgb2YgZWFjaCBjb21iaW5hdGlvbgpmcmVxX0NBX3Nob3J0IDwtIGRhdGFfQ0NBICU+JQogIHNlbGVjdChQcmVmZXJlZC5IYWJpdGF0LlR5cGUsIEZhbWlseSkgJT4lCiAgY291bnQoUHJlZmVyZWQuSGFiaXRhdC5UeXBlLCBGYW1pbHkpICU+JSAgCiAgcGl2b3Rfd2lkZXIoCiAgICBuYW1lc19mcm9tID0gRmFtaWx5LCAKICAgIHZhbHVlc19mcm9tID0gbiwgIAogICAgdmFsdWVzX2ZpbGwgPSAwKQoKIyBDb252ZXJ0IHRvIGRhdGFmcmFtZSBhbmQgY3JlYXRlIHJvd25hbWVzIChmb3IgdXNlIGNyZWF0aW5nIGF2ZXJhZ2UgcHJvZmlsZSkKZnJlcV9DQV9zaG9ydF9kZiA8LSBmcmVxX0NBX3Nob3J0ICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUgIAogIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAiUHJlZmVyZWQuSGFiaXRhdC5UeXBlIikgIAoKZnJlcV9DQV9sb25nCmZyZXFfQ0Ffc2hvcnQKZnJlcV9DQV9zaG9ydF9kZgpgYGAKCiMjIyBSZXByZXNlbnQgaW4gZ2dwbG90CmBgYHtyfQpnZ3Bsb3QoZnJlcV9DQV9sb25nKSArCiAgYWVzKHggPSBGYW1pbHksIHkgPSBIYWJpdGF0LCBmaWxsID0gRnJlcSkgKyAjIGZpbGwgc2F5cyB3aGljaCBjb2x1bW4gaXMgdXNlZCAKICAjIGZvciBmaWxsaW5nCiAgZ2VvbV9yYXN0ZXIoKSArICMgcmVwcmVzZW50cyBpbiByYXN0ZXIgbW9kZQogICMgYW5kIGRyZXNzIHRvIG1ha2UgaXQgcHJldHR5CiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArICMgQ2hhbmdlIGNvbG9yIHNjYWxlCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKYGBgCgojIyMgQ2FsY3VsYXRlIHRoZSBBdmVyYWdlIFByb2ZpbGUKYGBge3J9CiMgQ3JlYXRlIGF2ZXJhZ2UgcHJvZmlsZSAgCmF2ZXJhZ2VfcHJvZmlsZSA8LSBjb2xTdW1zKGZyZXFfQ0Ffc2hvcnRfZGYpIC8gc3VtKGZyZXFfQ0Ffc2hvcnRfZGYpCmF2ZXJhZ2VfcHJvZmlsZQoKIyBDcmVhdGUgdGhlIENvdW50ICAKY291bnRfaGFiaXRhdCA8LSByb3dTdW1zKGZyZXFfQ0Ffc2hvcnRfZGYpCmNvdW50X2hhYml0YXQKCiMgRGl2aXNpb24gQ291bnRzCkNBX3JlcGFydGl0aW9uIDwtIChmcmVxX0NBX3Nob3J0X2RmIC8gY291bnRfaGFiaXRhdCkgJT4lIAogIHJiaW5kKEF2ZXJhZ2VfcHJvZmlsZSA9IGF2ZXJhZ2VfcHJvZmlsZSkKQ0FfcmVwYXJ0aXRpb24KCmdncGxvdChmcmVxX0NBX2xvbmcpICsKICBhZXMoeCA9IEZhbWlseSwgeSA9IEhhYml0YXQsIGZpbGwgPSBGcmVxKSArICMgZmlsbCBzYXlzIHdoaWNoIGNvbHVtbiBpcyB1c2VkIAogICMgZm9yIGZpbGxpbmcKICBnZW9tX3Jhc3RlcigpICsgIyByZXByZXNlbnRzIGluIHJhc3RlciBtb2RlCiAgIyBhbmQgZHJlc3MgdG8gbWFrZSBpdCBwcmV0dHkKICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsgIyBDaGFuZ2UgY29sb3Igc2NhbGUKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQpgYGAKCiMjIyBDQQpgYGB7cn0KIyAKdW5pcXVlKGRhdGEkRmFtaWx5KQpyZXN1bHRfQ0EgPC0gQ0EoZnJlcV9DQV9zaG9ydF9kZiwgZ3JhcGggPSBGQUxTRSkKZnZpel9laWcocmVzdWx0X0NBKQpmdml6X2NhX2JpcGxvdChyZXN1bHRfQ0EsIHJlcGVsID0gVFJVRSkKYGBgCgojIyBDYW5vbmljYWwgQ29ycmVzcG9uZGFuY2UgQW5hbHlzaXMKIyMjIENyZWF0ZSBEYXRhCmBgYHtyfQptZWFuX2xhdGl0dWRlX2RmIDwtIGRhdGFfQ0NBICU+JQogIGdyb3VwX2J5KFByZWZlcmVkLkhhYml0YXQuVHlwZSkgJT4lICAgICAgICAgICAKICBzdW1tYXJpc2UobWVhbl9sYXRpdHVkZV9hYnMgPSBtZWFuKGFicyhMYXRpdHVkZSksIG5hLnJtID0gVFJVRSkpICU+JSAKICBhcy5kYXRhLmZyYW1lKCkgICAKCm1lYW5fbGF0aXR1ZGVfZGYKYGBgCgojIyMgUnVuIENDQQpgYGB7cn0KIyBNYWtlIGEgZnJlcXVlbmN5IHRhYmxlIGNvbnRhaW5pbmcgYWxsIDQgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRvIGJlIHVzZWQKcmVzdWx0X0NDQSA8LSBjY2EoZnJlcV9DQV9zaG9ydF9kZiB+IG1lYW5fbGF0aXR1ZGVfYWJzLAogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBtZWFuX2xhdGl0dWRlX2RmKQpwbG90KHJlc3VsdF9DQ0EpCgpzY29yZXNfQ0NBID0gc2NvcmVzKHJlc3VsdF9DQ0EpCm9yZGlBcnJvd011bChzY29yZXNfQ0NBJGJpcGxvdCkKYW5vdmEocmVzdWx0X0NDQSkKYW5vdmEocmVzdWx0X0NDQSxieT0ibWFyZ2luIikKYGBgCgoKCgoK